package states;

import static handlers.B2dVars.PPM;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.objects.EllipseMapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer.Cell;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.badlogic.gdx.math.Ellipse;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.ChainShape;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.Filter;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.MassData;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.utils.Array;
import com.me.mygdxgame.Game;

import entities.Cloud;
import entities.Crystal;
import entities.HUD;
import entities.Player;
import handlers.B2dVars;
import handlers.BackGround;
import handlers.BoundedCamera;
import handlers.GameStateManager;
import handlers.MyContactListener;
import handlers.MyInput;

public class Play extends GameState {

	// private BitmapFont font = new BitmapFont();
	private World world;
	private Box2DDebugRenderer b2dr;
	private BoundedCamera b2dCam;

	private MyContactListener cl;
	private Player player;
	private Array<Crystal> listscystal;
	private Array<Cloud> listcloud;
	// map ne
	private TiledMap titleMap;
	private float tileSize;
	private int tileMapWidth;
	private int tileMapHeight;

	private OrthogonalTiledMapRenderer tmr;
	private boolean debug = false;// ve cac duong vien bao quang doi tuong hay
	private HUD hud;

	public static int level = 1;
	public Vector2 poscamera;// chay theo pos cua nhan vat khi no chua di den
								// endmap - 1/2 width camera

	public Play(GameStateManager gsm) {
		super(gsm);
		// set up box2d stuff
		world = new World(new Vector2(0, -9.81f), true);
		cl = new MyContactListener();
		world.setContactListener(cl);
		b2dr = new Box2DDebugRenderer();
		poscamera = new Vector2();

		// create player
		createPlayer();
		// create tiles
		createTiles();
		cam.setBounds(0, tileMapWidth * tileSize, 0, tileMapHeight * tileSize);

		createCrystal();
		player.setTotalCrystal(listscystal.size);
		// CreateBackGround();
		createCloud();

		// set box2d came
		b2dCam = new BoundedCamera();
		b2dCam.setToOrtho(false, Game.V_WIDTH / PPM, Game.V_HEIGHT / PPM);
		b2dCam.setBounds(0, (tileMapWidth * tileSize) / PPM, 0,
				(tileMapHeight * tileSize) / PPM);
		// set up hud
		hud = new HUD(player);

	}

	private void playerJump() {
		if (cl.playerCanJump()) {
			player.getBody().setLinearVelocity(
					player.getBody().getLinearVelocity().x, 0);
			player.getBody().applyForceToCenter(0, 250, true);
			Game.res.getSound("jump").play();
		}
	}

	@Override
	public void handleInput() {
		// player jump
		if (MyInput.isPressed(Keys.X)) {
			playerJump();
		}
		if (MyInput.isPressed(Keys.C)) {
			switchBlocks();
		}

		// mouse/touch input for android
	
		// left side of screen to switch blocks
		// right side of screen to jump
		if (MyInput.isPressed()) {
			if (MyInput.x < Gdx.graphics.getWidth() / 2) {
				switchBlocks();
			} else {
				playerJump();
			}
		}
	}

	@Override
	public void update(float dt) {
		handleInput();
		world.step(Game.STEP, 1, 1);
		// remove crystal
		Array<Body> bodies = cl.getBodiesToRemove();
		for (int i = 0; i < bodies.size; i++) {
			Body b = bodies.get(i);
			listscystal.removeValue((Crystal) b.getUserData(), true);
			world.destroyBody(b);
			player.collectCrystal();
			Game.res.getSound("crystal").play();
		}
		bodies.clear();
		player.update(dt);

		// win
		if (player.getBody().getPosition().x * PPM > tileMapWidth * tileSize) {
			Game.res.getSound("levelselect").play();
			gsm.setState(GameStateManager.LEVEL_SELECT);
		}
		// chet
		if (player.getBody().getPosition().y < 0) {
			Game.res.getSound("hit").play();
			gsm.setState(GameStateManager.MENU);
		}
		if (player.getBody().getLinearVelocity().x < 0.001f) {
			Game.res.getSound("hit").play();
			gsm.setState(GameStateManager.MENU);
		}
		if (cl.isPlayerDead()) {
			Game.res.getSound("hit").play();
			gsm.setState(GameStateManager.MENU);
		}
		for (int i = 0; i < listscystal.size; i++) {
			listscystal.get(i).update(dt);
		}

		for (int i = 0; i < listcloud.size; i++) {
			listcloud.get(i).update(dt);
		}

	}

	@Override
	public void render() {
		// set camara to player
		// chay den gan cuoi map thi ko keo camrea theo nua
		if (player.getPostion().x * PPM < tileMapWidth * tileSize
				- Game.V_WIDTH + Game.V_WIDTH / 4) {
			poscamera.x = player.getPostion().x;
		}
		cam.position.set(poscamera.x * PPM + Game.V_WIDTH / 4,
				Game.V_HEIGHT / 2, 0);
		cam.update();
		// draw bgs
		// sp.setProjectionMatrix(HudCam.combined);
		// for (int i = 0; i < backgrounds.length; i++) {
		// backgrounds[i].render(sp);
		// }

		// draw map
		tmr.setView(cam);
		tmr.render();

		sp.setProjectionMatrix(cam.combined);
		player.render(sp);
		for (int i = 0; i < listscystal.size; i++) {
			listscystal.get(i).render(sp);
		}
		for (int i = 0; i < listcloud.size; i++) {
			listcloud.get(i).render(sp);
		}
		//
		sp.setProjectionMatrix(HudCam.combined);
		hud.render(sp);

		if (debug) {
			b2dr.render(world, b2dCam.combined);
		}
	}

	@Override
	public void dispose() {
		// TODO Auto-generated method stub

	}

	private void createPlayer() {
		BodyDef bdef = new BodyDef();
		PolygonShape shape = new PolygonShape();
		FixtureDef fdef = new FixtureDef();

		// create falling box
		bdef.position.set(0 / PPM, 50 / PPM);
		bdef.type = BodyType.DynamicBody;
		bdef.linearVelocity.set(1f, 0);
		Body body = world.createBody(bdef);
		shape.setAsBox(13 / PPM, 13 / PPM);

		fdef.shape = shape;
		// fdef.restitution = 1f;// dong nay de no bi nay len khi va cham duoi
		fdef.filter.categoryBits = B2dVars.BIT_PLAYER;
		fdef.filter.maskBits = B2dVars.BIT_RED | B2dVars.BIT_CRYSTAL;
		body.createFixture(fdef).setUserData("player");

		// create foot sensor
		shape.setAsBox(3 / PPM, 2 / PPM, new Vector2(0, -13 / PPM), 0);
		fdef.shape = shape;
		fdef.filter.categoryBits = B2dVars.BIT_PLAYER;
		fdef.filter.maskBits = B2dVars.BIT_RED;
		fdef.isSensor = false;
		body.createFixture(fdef).setUserData("foot");

		// create player
		player = new Player(body);
		body.setUserData("player");

		// final tweaks, manually set the player body mass to 1 kg
		MassData md = body.getMassData();
		md.mass = 1;
		body.setMassData(md);
	}

	private void createTiles() {

		// load map
		try {
			titleMap = new TmxMapLoader().load("maps/level" + level + ".tmx");
		} catch (Exception e) {
			System.out.println("Cannot find file: maps/level" + level + ".tmx");
			Gdx.app.exit();
		}
		tileMapWidth = Integer.valueOf(titleMap.getProperties().get("width")
				.toString());
		tileMapHeight = Integer.valueOf(titleMap.getProperties().get("height")
				.toString());
		tileSize = Integer.valueOf(titleMap.getProperties().get("tilewidth")
				.toString());

		tmr = new OrthogonalTiledMapRenderer(titleMap);
		TiledMapTileLayer layer;
		// tao body cho nen mau do va tao cung bao quanh no de xet va cham
		layer = (TiledMapTileLayer) titleMap.getLayers().get("red");
		CreateLayer(layer, B2dVars.BIT_RED);
		layer = (TiledMapTileLayer) titleMap.getLayers().get("green");
		CreateLayer(layer, B2dVars.BIT_GREEN);
		layer = (TiledMapTileLayer) titleMap.getLayers().get("pink");
		CreateLayer(layer, B2dVars.BIT_BlUE);

	}

	private void CreateLayer(TiledMapTileLayer layer, short bits) {

		BodyDef bdef = new BodyDef();
		FixtureDef fdef = new FixtureDef();

		for (int row = 0; row < layer.getHeight(); row++) {
			for (int col = 0; col < layer.getWidth(); col++) {
				// get cell
				Cell cell = layer.getCell(col, row);
				// check if cell exit
				if (cell == null)
					continue;
				if (cell.getTile() == null)
					continue;

				// creat body + fixture from cell
				bdef.type = BodyType.StaticBody;
				bdef.position.set((float) (col + 0.5f) * tileSize / PPM,
						(float) ((row + 0.5) * tileSize / PPM));

				ChainShape cs = new ChainShape();
				Vector2[] v = new Vector2[3];
				v[0] = new Vector2(-tileSize / 2 / PPM, -tileSize / 2 / PPM);
				v[1] = new Vector2(-tileSize / 2 / PPM, tileSize / 2 / PPM);
				v[2] = new Vector2(tileSize / 2 / PPM, tileSize / 2 / PPM);

				cs.createChain(v);
				fdef.friction = 0;
				fdef.shape = cs;
				fdef.filter.categoryBits = bits;
				fdef.filter.maskBits = B2dVars.BIT_PLAYER;
				fdef.isSensor = false;
				world.createBody(bdef).createFixture(fdef);
			}
		}
	}

	private void createCrystal() {

		listscystal = new Array<Crystal>();
		// lay layer co ten crystals trong map ra
		MapLayer layer = titleMap.getLayers().get("crystal");

		BodyDef bdef = new BodyDef();
		FixtureDef fdef = new FixtureDef();
		for (MapObject mo : layer.getObjects()) {
			bdef.type = BodyType.StaticBody;
			if (mo instanceof EllipseMapObject) {
				Ellipse e = ((EllipseMapObject) mo).getEllipse();
				float x = e.x / PPM;
				float y = e.y / PPM;
				bdef.position.set(x, y);
			}

			// // map object is a rectangle
			// if(mo instanceof RectangleMapObject) {
			// Rectangle r = ((RectangleMapObject) mo).getRectangle();
			// float x = r.x / PPM;
			// float y = r.y / PPM;
			// float width = r.width / PPM;
			// float height = r.height / PPM;
			// }

			// float x =0; //(float) mo.getProperties().get("x") /PPM;
			// float y = 0;//(float) mo.getProperties().get("y") /PPM;

			CircleShape cshape = new CircleShape();
			cshape.setRadius(8 / PPM);

			fdef.shape = cshape;
			fdef.isSensor = true;
			fdef.filter.categoryBits = B2dVars.BIT_CRYSTAL;
			fdef.filter.maskBits = B2dVars.BIT_PLAYER;

			Body body = world.createBody(bdef);
			body.createFixture(fdef).setUserData("crystal");

			Crystal c = new Crystal(body);
			listscystal.add(c);
			body.setUserData(c);
		}
	}

	private void createCloud() {

		listcloud = new Array<Cloud>();
		// lay layer co ten crystals trong map ra
		MapLayer layer = titleMap.getLayers().get("cloud");

		BodyDef bdef = new BodyDef();
		FixtureDef fdef = new FixtureDef();
		for (MapObject mo : layer.getObjects()) {
			bdef.type = BodyType.StaticBody;
			if (mo instanceof EllipseMapObject) {
				Ellipse e = ((EllipseMapObject) mo).getEllipse();
				float x = e.x / PPM;
				float y = e.y / PPM;
				bdef.position.set(x, y);
			}

			// // map object is a rectangle
			// if(mo instanceof RectangleMapObject) {
			// Rectangle r = ((RectangleMapObject) mo).getRectangle();
			// float x = r.x / PPM;
			// float y = r.y / PPM;
			// float width = r.width / PPM;
			// float height = r.height / PPM;
			// }

			// float x =0; //(float) mo.getProperties().get("x") /PPM;
			// float y = 0;//(float) mo.getProperties().get("y") /PPM;

			CircleShape cshape = new CircleShape();
			cshape.setRadius(10 / PPM);

			fdef.shape = cshape;
			fdef.isSensor = true;
			fdef.filter.categoryBits = B2dVars.BIT_CRYSTAL;
			// fdef.filter.maskBits = B2dVars.BIT_PLAYER;

			Body body = world.createBody(bdef);
			body.createFixture(fdef).setUserData("cloud");

			Cloud c = new Cloud(body);
			listcloud.add(c);
			body.setUserData(c);
		}
	}

	private void switchBlocks() {
		Filter filter = player.getBody().getFixtureList().first()
				.getFilterData();
		short bits = filter.maskBits;

		// switch to next color
		// red -> green -> blue -> red
		if ((bits & B2dVars.BIT_RED) != 0) {
			bits &= ~B2dVars.BIT_RED;
			bits |= B2dVars.BIT_GREEN;
		} else if ((bits & B2dVars.BIT_GREEN) != 0) {
			bits &= ~B2dVars.BIT_GREEN;
			bits |= B2dVars.BIT_BlUE;
		} else if ((bits & B2dVars.BIT_BlUE) != 0) {
			bits &= ~B2dVars.BIT_BlUE;
			bits |= B2dVars.BIT_RED;
		}

		// set new mask bits
		filter.maskBits = bits;
		player.getBody().getFixtureList().first().setFilterData(filter);

		// set new mask bits for foot
		filter = player.getBody().getFixtureList().get(1).getFilterData();
		bits &= ~B2dVars.BIT_CRYSTAL;
		filter.maskBits = bits;
		player.getBody().getFixtureList().get(1).setFilterData(filter);
		// play sound
		Game.res.getSound("changeblock").play();

	}
}